<template>
    <div class="console-page" style="display: flex;padding:0 5px 5px">
        <div class="title-page" @contextmenu.prevent.native>
            <div v-show="showSearch" style="margin-bottom: 6px;margin-top: 1px!important;">
                <div class="search forbid-select">
                    <div style="position: relative;width: 100%">
                        <input class="el-input__inner" placeholder="需要搜索的关键字" v-model="searchKey"
                            @keydown.esc="searchKey = ''" @keydown.enter="getData" />
                        <div class="searchType cursor-pointer" @click.prevent="ignoreCase = !ignoreCase"
                            :style="`top: 2px;color:${!ignoreCase ? 'rgb(200,0,0)' : ''}!important`">
                            区分大小写
                        </div>
                        <div class="searchType cursor-pointer" @click.prevent="vague = !vague"
                            :style="`bottom: 2px;color:${!vague ? 'rgb(200,0,0)' : ''}!important`">
                            全词匹配
                        </div>
                    </div>
                    <div class="el-input-group__append" style="width: 5px;height: 40px;" @click.prevent="getData">
                        <el-icon>
                            <component :is="getIcon('Refresh')" />
                        </el-icon>
                    </div>
                </div>
            </div>
            <div class="template"
                :style="`height:calc(100% - ${showSearch ? '52px' : '3px'});border: 1px solid #e4e7ec;border-radius: 5px`">
                <el-scrollbar style="height: 100%;width: 100%">
                    <el-tree class="forbid-select" node-key="name" :props="props" ref="treeRef"
                        :show-checkbox="showCheckBox" :data="cacheData" @node-click="click" @check="clickCheckBox"
                        @node-contextmenu="" highlight-current>
                        <template #default="{ node, data }">
                            <span style="width: 100%; padding: 6px 0;" @mouseenter="mouseenter(data)"
                                @mouseleave="mouseleave(data)">
                                <span class="item" :style="`max-width:calc(100% - ${showCheckBox ? '70px' : '50px'}`">
                                    <span class="itemText font-auto-resize">
                                        {{ data.name }}
                                    </span>
                                    <el-button v-show="data.show" @click.prevent.stop="deleteCache(data)"
                                        class="delete button-auto-resize" link :icon="getIcon('close')"
                                        v-permission="$permission.Delete" />
                                </span>
                            </span>

                        </template>
                    </el-tree>
                </el-scrollbar>
            </div>
        </div>
        <div style="width: calc(100% - var(--font-size) * 20 - 95px);height: calc(100% - 6px);">
            <transition :name="init ? 'animate__animated ' : ''"
                enter-active-class="animate__fadeInLeft animate__faster"
                leave-active-class="animate__fadeOutRight animate__faster" mode="out-in">
                <div :key="currentKeyInfo.name" class="full-percent">
                    <div ref="minusRef" v-if="currentKeyInfo.name">
                        <el-card :body-style="{ padding: '0px' }" class="cache-title" shadow="never"
                            style="min-width: 220px">
                            <el-form :model="currentKeyInfo" status-icon ref="cacheInfoRef" spellcheck="false"
                                @submit.native.prevent>
                                <div class="cache-info">
                                    <el-form-item label="key :" prop="type" label-width="80px" class="cache-key">
                                        <div class="no-wrap">{{ currentKeyInfo.name }}</div>
                                    </el-form-item>
                                    <el-form-item label="type :" prop="type" label-width="80px" class="cache-type">
                                        <div>{{ currentKeyInfo.type }}</div>
                                    </el-form-item>
                                    <el-form-item label="ttl :" prop="ttl" label-width="90px" class="cache-ttl">
                                        <div>{{ currentKeyInfo.ttl }}</div>
                                    </el-form-item>
                                    <el-form-item label="size :" prop="size" label-width="90px" class="cache-size">
                                        <div>{{ cacheSize }}</div>
                                    </el-form-item>
                                </div>

                            </el-form>
                        </el-card>
                    </div>
                    <div v-else class="cache-placeholder">选择对应的key展示缓存数据</div>
                    <div v-if="currentKeyInfo.type === 'HASH'">
                        <div style="margin-bottom: 1px">
                            <el-table :data="Object.keys(currentKeyData || {})" border onselectstart="return false"
                                ref="tableRef" class="e-page cache-table" :height="`calc(25vh + ${dragDelta}px)`"
                                @row-click="hashClick" highlight-current-row cell-class-name="cursor-default table-cell"
                                row-class-name="table-row">
                                <el-table-column type="index" label="row" width="150px" key="c1" />
                                <el-table-column prop="key" label="key" width="200px" key="c2">
                                    <template #default="scope">
                                        {{ scope.row }}
                                    </template>
                                </el-table-column>
                                <el-table-column prop="value" label="value" min-width="300px" key="c3">
                                    <template #default="scope">
                                        {{ currentKeyData[scope.row] }}
                                    </template>
                                </el-table-column>
                            </el-table>
                        </div>
                        <div class="drag cursor-pointer" @mousedown="startDrag" />
                        <div style="position: relative">
                            <div class="code-high"
                                :style="`height: calc(100vh - 25vh - ${dragDelta}px - var(--font-size) * 4 - 89px);`"
                                ref="codeRef" v-loading="loading" element-loading-text="数据查询中">
                                <highlight :code="formatJson(valueData) || ''"
                                    :language="isJsonString(valueData) ? 'json' : 'java'" />
                                <div class="byte">size:{{ formatSize(encoder.encode(valueData).length) }}</div>
                            </div>
                        </div>
                    </div>
                    <div v-if="currentKeyInfo.type === 'STRING'" v-loading="loading" element-loading-text="数据查询中"
                        style="height: calc(100vh - var(--font-size) * 4 - 83px);margin-top: 5px;border: 1px solid rgb(228, 231, 236);border-radius: 5px;position: relative">
                        <highlight :code="formatJson(currentKeyData) || ''"
                            :language="isJsonString(currentKeyData) ? 'json' : 'java'" />
                        <div class="byte">size:{{ formatSize(encoder.encode(currentKeyData).length) }}</div>
                    </div>
                    <div v-if="currentKeyInfo.type === 'NONE'"
                        style="height: calc(100vh - var(--font-size) * 4 - 83px);margin-top: 5px;border: 1px solid rgb(228, 231, 236);border-radius: 5px;position: relative">
                        <div class="noneKey">缓存内容已不存在</div>
                    </div>
                    <div v-if="currentKeyInfo.type === 'LIST'">
                        <div style="margin-bottom: 1px">
                            <el-table :data="currentKeyData || []" border :height="`calc(25vh + ${dragDelta}px)`"
                                cell-class-name="cursor-default table-cell" row-class-name="table-row"
                                class="e-page cache-table" @row-click="listClick" ref="tableRef"
                                onselectstart="return false" highlight-current-row>
                                <el-table-column type="index" label="row" key="c5" width="100px" />
                                <el-table-column prop="value" label="value" min-width="200px" key="c4">
                                    <template #default="scope">
                                        {{ scope.row.substr(0, 1000) }}
                                    </template>
                                </el-table-column>
                            </el-table>
                        </div>
                        <div class="drag cursor-pointer" @mousedown="startDrag" />
                        <div style="position: relative">
                            <div class="code-high"
                                :style="`height: calc(100vh - 25vh - ${dragDelta}px - var(--font-size) * 4 - 89px);`"
                                ref="codeRef" v-loading="loading" element-loading-text="数据查询中">
                                <highlight :code="formatJson(valueData) || ''"
                                    :language="isJsonString(valueData) ? 'json' : 'java'" />
                                <div class="byte">size:{{ formatSize(encoder.encode(valueData).length) }}</div>
                            </div>
                        </div>
                    </div>
                </div>
            </transition>
        </div>
    </div>

</template>

<script setup>
import highlight from "@/components/editor/highlight"
import { formatJson, isJsonString, formatSize, getElementSize, getIcon } from "@/utils/tools"
import { ElMessageBox } from "element-plus"
import { useElMessage, useModalSize, useRequest, useEventListener } from "@usehook"

const encoder = new TextEncoder()

let props = $ref({
    label: 'name',
    isLeaf: 'leaf'
})
let cacheData = $ref([])
let currentKeyInfo = $ref({ name: '', type: '', ttl: '' })
let currentKeyData = $ref(null)
let checkChild = $ref([])
let showCheckBox = $ref(false)
let showSearch = $ref(true)
let searchKey = $ref("")
let ignoreCase = $ref(true)
let vague = $ref(true)
let valueData = $ref("")
let curRow = $ref(1)
let init = $ref(false)
let draggable = $ref(false)
let move = $ref({ start: 0, offset: 0, cacheDelta: 0 })
let dragDelta = $ref(0)
let loading = $ref(false)
let page = $ref(1)

const treeRef = $ref(null)
const tableRef = $ref(null)
const codeRef = $ref(null)

const cacheSize = computed(() => currentKeyInfo.type === 'LIST' ? (currentKeyData || '').length
    : currentKeyInfo.type === 'HASH' ? Object.keys(currentKeyData || {}).length : 0)

const onDrag = (e) => {
    if (draggable) {
        let delta = e.clientY - move.start
        if (getElementSize(tableRef.$el, "height") < 100 && delta < move.cacheDelta) return
        if (getElementSize(codeRef, "height") < 100 && delta > move.cacheDelta) return
        dragDelta = delta + move.offset
        move.cacheDelta = delta
    }
}

const endDrag = () => {
    draggable = false
    // 允许用户选择网页中文字
    document.onselectstart = null
}

const startDrag = (e) => {
    move.offset = dragDelta
    draggable = true
    move.start = e.clientY
    move.cacheDelta = 0
    // 禁止用户选择网页中文字
    document.onselectstart = () => false
}

const hashClick = (row) => {
    valueData = currentKeyData[row] || ''
    curRow = Object.keys(currentKeyData).indexOf(row) + 1
}

const listClick = (row) => {
    valueData = row || ''
    curRow = currentKeyData.indexOf(row) + 1
}

//加载实例下的缓存信息
const getData = () => {
    currentKeyInfo = {}
    currentKeyData = null
    checkChild = []
    useRequest({
        url: '/data/redis/keys',
        key: encodeURI(searchKey),
        ignoreCase: ignoreCase,
        vague: vague
    }).then(resp => {
        if (resp && resp.status) {
            cacheData = resp.data.map(e => {
                return { name: e, leaf: true }
            }).sort((a, b) => {
                return a.name[0].toLowerCase().localeCompare(b.name[0].toLowerCase())
            })
        } else {
            const message = resp ? resp.message : ""
            if (message) useElMessage.error(message)
        }
    }).catch(error => {
        console.log(error, 1)
    })
}

//点击key信息加载对应的实时信息
const click = (obj) => {
    page = 1
    if (loading) return
    let _currentKeyInfo = {}
    for (let data of cacheData) {
        if (data.name === obj.name) {
            _currentKeyInfo = data
        }
    }
    if (_currentKeyInfo && currentKeyInfo && _currentKeyInfo.name !== currentKeyInfo.name) {
        currentKeyData = null
        valueData = ""
    }
    currentKeyInfo = _currentKeyInfo
    curRow = 1

    loading = true
    useRequest({ url: '/data/redis/cache', key: obj.name, page: page, pageSize: 2000 }).then((r) => {
        if (r && r.status) {
            currentKeyData = r.data.data
            const type = r.data.type
            if (type === 'LIST') {
                valueData = (currentKeyData || [""])[0]
            } else if (type === 'HASH') {
                valueData = Object.values(currentKeyData || { "key": "" })[0]
            }
            currentKeyInfo.ttl = r.data.expire
            currentKeyInfo.type = type
        }
    }).catch((err) => {
        console.log(err, 2)
    }).finally(() => {
        loading = false
    })
}

const mouseenter = (data) => {
    data.show = true
}
const mouseleave = (data) => {
    data.show = false
}

//删除单个缓存信息
const deleteCache = (data) => {
    const name = data.name.length > 30 ? data.name.substr(0, 30) + "..." : data.name
    ElMessageBox.confirm('确定删除缓存「' + name + '」?', '提示', {
        dangerouslyUseHTMLString: true,
        confirmButtonText: ' ',
        cancelButtonText: ' ',
        type: 'warning',
        customClass: "c-dialog forbid-select",
        closeOnClickModal: false,
        cancelButtonClass: "iconfont icon-close button-auto-resize",
        confirmButtonClass: 'iconfont icon-check button-auto-resize',
        roundButton: true,
        showClose: false,
    }).then(() => {
        useRequest({ url: '/data/redis/delete', key: data.name, method: "post" }).then((response) => {
            if (response && response.status) {
                getData()
            }
        }).catch((error) => {
            console.log(error, 3)
        })
    }).catch(_ => _).finally(_ => _)

}
//批量删除缓存
const batchDel = () => {
    ElMessageBox.confirm('确定删除缓存选中的' + checkChild.length + '个缓存?', '提示', {
        dangerouslyUseHTMLString: true,
        confirmButtonText: ' ',
        cancelButtonText: ' ',
        type: 'warning',
        customClass: "c-dialog forbid-select",
        closeOnClickModal: false,
        cancelButtonClass: "iconfont icon-close button-auto-resize",
        confirmButtonClass: 'iconfont icon-check button-auto-resize',
        roundButton: true,
        showClose: false,
    }).then(_ => {
        useRequest({ url: '/data/redis/batchDel', key: checkChild.join(","), method: "post" }).then((response) => {
            if (response && response.status) {
                if (response.message) useElMessage(response.message)
                treeRef.setCheckedNodes([])
                checkChild = []
                getData()
            }
        }).catch((error) => {
            console.log(error, 4)
        })
    }).catch(_ => _).finally(_ => _)

}

//点击复选框时触发事件
const clickCheckBox = (_nodeData, nodeStatus) => {
    checkChild = nodeStatus.checkedKeys
}

const keyMap = (e) => {
    let { key, keyCode } = e
    if (!useModalSize.get()) {
        if ((key === 'Backspace' || keyCode === 8 || key === 'Delete' || key === 46) && showCheckBox && checkChild.length) {
            batchDel()
        }
        if (keyCode === 114 || key === 'F3') {
            e.preventDefault() //关闭浏览器快捷键
            treeRef.setCheckedNodes([])
            checkChild = []
            showCheckBox = !showCheckBox
        }
        if (keyCode === 113 || key === 'F2') {
            e.preventDefault()
            showSearch = !showSearch
        }
        if (keyCode === 123 || key === "F12") {
            // e.preventDefault()
        }
    }
}

onMounted(() => {
    getData()
    setTimeout(() => {
        init = true
    }, 500)

})

useEventListener("mousemove", onDrag)
useEventListener("mouseup", endDrag)
useEventListener('keydown', keyMap)
</script>

<style scoped>
:deep(.el-input.is-disabled .el-input__inner) {
    cursor: default;
    color: #323229;
    background-color: #FFF
}

:deep(.el-scrollbar__wrap) {
    overflow-x: hidden;
}

:deep(.el-form-item__label) {
    color: crimson;
    line-height: calc(var(--font-size) + 10px);
    font-size: var(--font-size);
    display: flex;
    align-items: center;
    justify-content: center;
}

:deep(.el-card__body .el-form-item) {
    margin-bottom: 0;
    display: flex;
    align-items: center;
}

:deep(.ace_editor) {
    margin: 5px 0 !important;
}

:deep(.el-scrollbar__bar.is-vertical > div) {
    width: 0 !important;
}

:deep(.el-tree-node__content) {
    /*margin-bottom: 6px !important;*/
    margin-left: 0 !important;
    padding: 0 !important;
    height: fit-content !important;
}

:deep(.search .el-input__inner) {
    height: 42px !important;
    border-radius: 4px 0 0 4px;
    padding: 5px 10px;
}

:deep(.item .delete) {
    position: absolute;
    right: 0;
    width: 35px;
    margin-top: 2px;
    display: none;
}

:deep(.el-tree-node:hover .item .delete) {
    display: block;
}

:deep(.el-tree) {
    background: rgba(255, 255, 255, .4);
}

:deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content) {
    background-color: rgba(255, 255, 255, .7) !important;
}

:deep(.item) {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 14px;
    padding-right: 8px;
    height: 100%;
}

:deep(.itemText) {
    white-space: nowrap !important;
    text-overflow: ellipsis !important;
    overflow: hidden !important;
    line-height: calc(var(--font-size) + 7px);
}

:deep(.searchType) {
    font-size: 13px;
    height: calc(50% - 2px);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: #000000;
    width: 90px;
    position: absolute;
    right: 2px;
}

:deep(.search .append) {
    display: flex;
    justify-content: center;
    align-items: center;
    height: calc(100% - 2px);
}

:deep(.search) {
    display: flex;
    position: relative;
}

:deep(.cache-table) {
    margin-top: 5px;
}

:deep(.row-select:hover > td) {
    background-color: transparent !important;
}

:deep(.row-select .el-table__cell) {
    border-right-color: transparent !important;
}

:deep(.row-select) {
    background: rgba(163, 177, 255, .6)
}

:deep(.code-high) {
    border: 1px solid rgb(228, 231, 236);
    border-radius: var(--border-radius);
    position: relative;
}

:deep(.byte) {
    position: absolute;
    right: 4px;
    bottom: 0;
    opacity: 1;
    z-index: 10;
    /*background: #f8f8f8;*/
    /*padding: 3px 5px;*/
    /*border-radius: 5px;*/
}

:deep(.el-scrollbar__bar.is-horizontal) {
    height: 0 !important;
    width: 0 !important;
}

:deep(.noneKey) {
    font-size: 30px;
    color: crimson;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    background: rgba(255, 255, 255, .7);
}

:deep(.drag) {
    height: 5px;
    cursor: n-resize;
    background: rgb(0 0 0 / 5%);
}

:deep(.el-loading-mask) {
    margin-top: 1px;
    border: 1px solid #ebeef5;
    border-radius: 5px;
}

:deep(.title-page) {
    padding-right: 5px;
    position: relative;
    z-index: 99;
    width: calc(var(--font-size) * 20 + 80px);
    height: calc(100% - 5px);
}

:deep(.el-input__inner) {
    border: 1px solid #c5c5c5;
}

:deep(.el-input-group__append) {
    border-left: 0 !important;
    border-top-left-radius: 0 !important;
    border-bottom-left-radius: 0 !important;
    border: 1px solid #c5c5c5;
    border-radius: 4px;
    background: transparent;
}

:deep(.cache-title) {
    background: rgba(255, 255, 255, .7);
}

:deep(.cache-placeholder) {
    background: transparent;
    width: 100%;
    height: 100%;
    border-radius: var(--border-radius);
    display: flex;
    justify-content: center;
    align-items: center;
}

:deep(.table-row) {
    height: 30px
}

:deep(.table-cell) {
    padding: 2px;
    cursor: default
}

:deep(.el-table th.el-table__cell) {
    background-color: rgb(203 203 203 / 40%);
}

:deep(.cache-info) {
    display: flex;
    width: 100%;
    align-items: center;
    height: calc(var(--font-size) * 2 + 10px);
    gap: 15px
}

:deep(.cache-key) {
    width: calc(100% - 555px);
}

:deep(.cache-type) {
    width: 160px;
}

:deep(.cache-ttl) {
    width: 200px;
}

:deep(.cache-size) {
    width: 150px;
}
</style>